home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 …ember: Reference Library / Dev.CD Dec 00 RL Disk 1.toast / pc / technical documentation / develop / develop issue 28 / develop issue 28 code / merge tools / io.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-25  |  37.4 KB  |  1,185 lines

  1. /* File I/O for Eclectus integration utilities.
  2.      Copyright (C) 1992-1996 Eclectus (D. John Anderson, Alan B. Harper).
  3.  
  4. This file is part of the Eclectus integration utilities.
  5.  
  6. Eclectus integration utilities are free software; you can redistribute
  7. it and/or modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 1, or
  9. (at your option) any later version.
  10.  
  11. Eclectus integration utilities is distributed in the hope that it
  12. will be useful, but WITHOUT ANY WARRANTY; without even the implied
  13. warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. See the GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with the Eclectus integration utilities; see the file COPYING.
  18. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge,
  19. MA 02139, USA.    */
  20.  
  21. #include "diff.h"
  22. #include <ctype.h>
  23. #include <string.h>
  24. #include <stddef.h>
  25. #include <stdlib.h>
  26.  
  27. #ifdef DF_MACHINE_MACINTOSH
  28.     #include <Errors.h>
  29.     #include <Files.h>
  30.     #include <CursorCtl.h>
  31. #endif
  32.  
  33. #ifdef DF_MACHINE_WINDOWS
  34.     #define WIN32_LEAN_AND_MEAN
  35.     #include <Windows.h>
  36. #endif
  37.  
  38. #ifdef DF_MACHINE_NEXT
  39.     #include <sys/types.h>
  40.     #include <sys/dir.h>
  41.     #include <sys/stat.h>
  42.     #include <sys/time.h>
  43.  /*
  44.     * These defines are normally in libc.h.  However, libc.h won't compile correctly
  45.     * when the ansi flag is on and there are too many problems to fix so we'll just declare
  46.     * these items here.
  47.     */
  48.     extern int    mkdir(const char *, int);
  49.     extern int    chmod(const char *, int); 
  50. #endif
  51.  
  52.  /*
  53.     * You can modify this list of extensions to specify which files are binary on machines
  54.     * other than Macintosh OS.  Macintosh stores attributes with a file which indicate
  55.     * binary files.
  56.   *
  57.     * You can also specify extensions to ignore.
  58.     *
  59.     * The program will treat the case of extensions as insignificant.
  60.     *
  61.     * Note: Remember to update MAX_EXTENSION_LENGTH to reflect the longest extension in both
  62.     * both lists.  You must enter these extension in lower case.  The last extension must
  63.     * be the empty string
  64.     */
  65.  
  66. #define MAX_EXTENSION_LENGTH 3
  67.  
  68. #ifndef DF_MACHINE_MACINTOSH
  69.     static char     *binaryExtensions [] = {
  70.         "aps",
  71.         "bmp",
  72.         "bsc",
  73.         "dll",
  74.         "exe",
  75.         "hlp",
  76.         "ico",
  77.         "ink",
  78.         "lib",
  79.         "mdp",
  80.         "mpw",
  81.         "mwk",
  82.         "ncb",
  83.         "obj",
  84.         "os",
  85.         "osx",
  86.         "pch",
  87.         "pdb",
  88.         "prj",
  89.         "res",
  90.         "sbr",
  91.         "scr",
  92.         "vcp",
  93.         "wri",
  94.         "8bf",
  95.         ""
  96.     };
  97. #endif
  98.  
  99. static char     *ignoreExtensions [] = {
  100.     "i",
  101.     "idb", /* MSVC 4.0 intermediate file */
  102.     "ilk", /* MSVC intermediate link file */
  103.     "ncb", /* MSVC 4.0 binary project file cache */
  104.     "obj", /* MSVC Object files */
  105.     "pch", /* MSVC precompiled header files */
  106.     "pdb", /* MSVC program database */
  107.     "tmp", /* MSVC temporary file */
  108.     ""
  109. };
  110.  
  111. /* Lines are put into equivalence classes (of lines that match in line_cmp).
  112.      Each equivalence class is represented by one of these structures,
  113.      but only while the classes are being computed.    
  114.      Afterward, each class is represented by a number.    */
  115. struct equivclass
  116. {
  117.     struct equivclass *next;            /* Next item in this bucket. */
  118.     struct line_def line; /* A line that fits this class. */
  119. };
  120.  
  121. /* Hash-table: array of buckets, each being a chain of equivalence classes.  */
  122. static struct equivclass **buckets;
  123.     
  124. /* Size of the bucket array. */
  125. static int nbuckets;
  126.  
  127. /* Array in which the equivalence classes are allocated.
  128.      The bucket-chains go through the elements in this array.
  129.      The number of an equivalence class is its index in this array.  */
  130. static struct equivclass *equivs;
  131.  
  132. /* Index of first free element in the array `equivs'.  */
  133. static int equivs_index;
  134.  
  135. /* Largest primes less than some power of two, for nbuckets.    Values range
  136.      from useful to preposterous.  If one of these numbers isn't prime
  137.      after all, don't blame it on me, blame it on primes (6) . . . */
  138. static int primes[] =
  139. {
  140.     509,
  141.     1021,
  142.     2039,
  143.     4093,
  144.     8191,
  145.     16381,
  146.     32749,
  147.     65521,
  148.     131071,
  149.     262139,
  150.     524287,
  151.     1048573,
  152.     2097143,
  153.     4194301,
  154.     8388593,
  155.     16777213,
  156.     33554393,
  157.     67108859,                                         /* Preposterously large . . . */
  158.     -1
  159. };
  160.  
  161. static void         find_and_hash_each_line (register struct file_data *filePtr);
  162. static void         find_equiv_class (register struct file_data *filePtr);
  163. static void         find_identical_ends (register struct file_data *file0Ptr, register struct file_data *file1Ptr);
  164. static int            PathHasExtension (const char *pathCharPtr, const char **pathExtensions);
  165.  
  166.  
  167.  /*
  168.     * This is a machine dependent routine that copies selected file fileAttributes
  169.     * from the file named sourceNamePtr to the file named destNamePtr according
  170.     * to fileAttributes, which may contain any of the following bits:
  171.     *        LOCK_MASK - locks the destination file
  172.     *   DATE_MASK - copies the date from the source file to the destination file
  173.     *        RESOURCE_MASK - copies the resource fork on Mac, copies the FILE_ATTRIBUTE_HIDDEN
  174.     *                                        and FILE_ATTRIBUTE_SYSTEM attribute on Windows and the execute
  175.     *                                        bit on NeXT.
  176.     * We all know that this routine isn't reliable because of "race conditions."
  177.     * The time that occurs between when you read a filename and when you access
  178.     * it allows the filename to change in a multi-user environment.  However, the
  179.     * typical brainless operating systems don't let you refer to files using an
  180.     * open reference number for many operations, such as those used in this
  181.     * routine.
  182.     */
  183.  
  184. #ifdef DF_MACHINE_MACINTOSH
  185. void
  186. CopyFileAttributes (const char *sourceNamePtr, const char *destNamePtr, const int fileAttributes)
  187. {
  188.     HParamBlockRec    destFInfoHParamBlock;
  189.     HParamBlockRec    destLockHParamBlock;
  190.     Str255                    destNamePString;
  191.     ParamBlockRec        destResourceParamBlk;
  192.     int                         ioResult;
  193.     int                            pathLength;
  194.     int                            sourceFInfoExists;
  195.     HParamBlockRec    sourceFInfoHParamBlock;
  196.     Str255                    sourceNamePString;
  197.     ParamBlockRec        sourceResourceParamBlk;
  198.  
  199.     if (fileAttributes != 0) {
  200.         pathLength = strlen (destNamePtr);
  201.         if (pathLength > FILENAME_MAX) {
  202.             fprintf (stderr, "%s\n", destNamePtr);
  203.             Error (PATH_TOO_LONG);
  204.         }
  205.         destNamePString [0] = (unsigned char) pathLength;
  206.         memcpy (destNamePString + 1, destNamePtr, pathLength);
  207.  
  208.         pathLength = strlen (sourceNamePtr);
  209.         if (pathLength > FILENAME_MAX) {
  210.             fprintf (stderr, "%s\n", sourceNamePtr);
  211.             Error (PATH_TOO_LONG);
  212.         }
  213.         sourceNamePString [0] = (unsigned char) pathLength;
  214.         memcpy (sourceNamePString + 1, sourceNamePtr, pathLength);
  215.  
  216.         sourceFInfoExists = FALSE;
  217.         if ((fileAttributes & (DATE_MASK | RESOURCE_MASK)) != 0) {
  218.             bzero (&destFInfoHParamBlock, sizeof (HParamBlockRec));
  219.             destFInfoHParamBlock.fileParam.ioNamePtr = destNamePString;
  220.             ioResult = PBHGetFInfoSync (&destFInfoHParamBlock);
  221.             if (ioResult != noErr)
  222.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, destNamePtr);
  223.  
  224.             bzero (&sourceFInfoHParamBlock, sizeof (HParamBlockRec));
  225.             sourceFInfoHParamBlock.fileParam.ioNamePtr = sourceNamePString;
  226.             ioResult = PBHGetFInfoSync (&sourceFInfoHParamBlock);
  227.             sourceFInfoExists = ioResult == noErr;
  228.         }
  229.  
  230.         if ((fileAttributes & RESOURCE_MASK) != 0) {
  231.             bzero (&sourceResourceParamBlk, sizeof (ParamBlockRec));
  232.             sourceResourceParamBlk.fileParam.ioNamePtr = sourceNamePString;
  233.             sourceResourceParamBlk.ioParam.ioPermssn = fsRdPerm;
  234.             ioResult = PBOpenRFSync (&sourceResourceParamBlk);
  235.             if (ioResult == noErr) { /* Don't copy if source doesn't exist */
  236.                 bzero (&destResourceParamBlk, sizeof (ParamBlockRec));
  237.                 destResourceParamBlk.fileParam.ioNamePtr = destNamePString;
  238.                 destResourceParamBlk.ioParam.ioPermssn = fsWrPerm;
  239.                 ioResult = PBOpenRFSync (&destResourceParamBlk);
  240.                 if (ioResult != noErr)
  241.                     ErrorWithStringArgument (UNEXPECTED_IO_ERROR, destNamePtr);
  242.                 ioResult = PBGetEOFSync (&sourceResourceParamBlk);
  243.                 if (ioResult != noErr)
  244.                     ErrorWithStringArgument (UNEXPECTED_IO_ERROR, sourceNamePtr);
  245.                 sourceResourceParamBlk.ioParam.ioBuffer = xmalloc ((size_t) sourceResourceParamBlk.ioParam.ioMisc);
  246.                 sourceResourceParamBlk.ioParam.ioReqCount = (long) sourceResourceParamBlk.ioParam.ioMisc;
  247.                 sourceResourceParamBlk.ioParam.ioPosMode = fsFromStart;
  248.                 ioResult = PBReadSync (&sourceResourceParamBlk);
  249.                 if (ioResult != noErr)
  250.                     ErrorWithStringArgument (UNEXPECTED_IO_ERROR, sourceNamePtr);
  251.                 destResourceParamBlk.ioParam.ioBuffer = sourceResourceParamBlk.ioParam.ioBuffer;
  252.                 destResourceParamBlk.ioParam.ioReqCount = (long) sourceResourceParamBlk.ioParam.ioMisc;
  253.                 destResourceParamBlk.ioParam.ioPosMode = fsFromStart;
  254.                 ioResult = PBWriteSync (&destResourceParamBlk);
  255.                 if (ioResult != noErr)
  256.                     ErrorWithStringArgument (UNEXPECTED_IO_ERROR, destNamePtr);
  257.                 free (sourceResourceParamBlk.ioParam.ioBuffer);
  258.                 ioResult = PBCloseSync (&sourceResourceParamBlk);
  259.                 if (ioResult != noErr)
  260.                     ErrorWithStringArgument (UNEXPECTED_IO_ERROR, sourceNamePtr);
  261.                 ioResult = PBCloseSync (&destResourceParamBlk);
  262.                 if (ioResult != noErr)
  263.                     ErrorWithStringArgument (UNEXPECTED_IO_ERROR, destNamePtr);
  264.             }
  265.             if (sourceFInfoExists) {
  266.                 destFInfoHParamBlock.fileParam.ioFlFndrInfo.fdType =
  267.                     sourceFInfoHParamBlock.fileParam.ioFlFndrInfo.fdType;
  268.                 destFInfoHParamBlock.fileParam.ioFlFndrInfo.fdCreator =
  269.                     sourceFInfoHParamBlock.fileParam.ioFlFndrInfo.fdCreator;
  270.                 destFInfoHParamBlock.fileParam.ioFlFndrInfo.fdLocation =
  271.                     sourceFInfoHParamBlock.fileParam.ioFlFndrInfo.fdLocation;
  272.             }
  273.         }
  274.         if ((fileAttributes & DATE_MASK) != 0 && sourceFInfoExists) {
  275.             destFInfoHParamBlock.fileParam.ioFlCrDat = sourceFInfoHParamBlock.fileParam.ioFlCrDat;
  276.             destFInfoHParamBlock.fileParam.ioFlMdDat = sourceFInfoHParamBlock.fileParam.ioFlMdDat;
  277.         }
  278.         if ((fileAttributes & LOCK_MASK) != 0) {
  279.             bzero (&destLockHParamBlock, sizeof (HParamBlockRec));
  280.             destLockHParamBlock.fileParam.ioNamePtr = destNamePString;
  281.             ioResult = PBHSetFLockSync (&destLockHParamBlock);
  282.             if (ioResult != noErr)
  283.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, destNamePtr);
  284.         }
  285.  
  286.         if ((fileAttributes & (DATE_MASK | RESOURCE_MASK)) != 0) {
  287.             destFInfoHParamBlock.fileParam.ioVRefNum = 0;
  288.             destFInfoHParamBlock.fileParam.ioDirID = 0;
  289.             ioResult = PBHSetFInfoSync (&destFInfoHParamBlock);
  290.             if (ioResult != noErr)
  291.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, destNamePtr);
  292.         }
  293.  
  294.     }
  295. }
  296. #endif
  297.  
  298. #ifdef DF_MACHINE_WINDOWS
  299. void
  300. CopyFileAttributes (const char *sourceNamePtr, const char *destNamePtr, const int fileAttributes)
  301. {
  302.     FILETIME    creationTime;
  303.     DWORD         destFileAttributes;
  304.     HANDLE         destFileHandle;
  305.     FILETIME    lastAccessTime;
  306.     FILETIME    lastWriteTime;
  307.     DWORD         sourceFileAttributes;
  308.     HANDLE         sourceFileHandle;
  309.  
  310.     if (fileAttributes != 0) {
  311.         if ((fileAttributes & DATE_MASK) != 0) {
  312.             sourceFileHandle = CreateFile(sourceNamePtr,                                /* file name pointer  */
  313.                                                                     GENERIC_READ,                                    /* open for reading   */
  314.                                                                     FILE_SHARE_READ,              /* share reads                */
  315.                                                                     (LPSECURITY_ATTRIBUTES) NULL, /* no security        */
  316.                                                                     OPEN_EXISTING,                /* existing file only */
  317.                                                                     FILE_ATTRIBUTE_NORMAL,        /* normal file        */
  318.                                       (HANDLE) NULL);               /* no attr. template  */
  319.             if (sourceFileHandle == INVALID_HANDLE_VALUE)
  320.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, sourceNamePtr);
  321.             if (!GetFileTime (sourceFileHandle, &creationTime,
  322.                                                                                     &lastAccessTime,
  323.                                                                                     &lastWriteTime))
  324.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, sourceNamePtr);
  325.             if (!CloseHandle (sourceFileHandle))
  326.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, sourceNamePtr);
  327.             destFileHandle = CreateFile (destNamePtr,                                     /* file name pointer  */
  328.                                                                  GENERIC_WRITE,                                 /* open for writing   */
  329.                                                                  FILE_SHARE_WRITE,             /* share writes       */
  330.                                                                    (LPSECURITY_ATTRIBUTES) NULL, /* no security        */
  331.                                                                    OPEN_EXISTING,                /* existing file only */
  332.                                                                    FILE_ATTRIBUTE_NORMAL,        /* normal file        */
  333.                                      (HANDLE) NULL);               /* no attr. template  */
  334.             if (destFileHandle == INVALID_HANDLE_VALUE)
  335.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, sourceNamePtr);
  336.             if (!SetFileTime (destFileHandle, &creationTime,
  337.                                                                                 &lastAccessTime,
  338.                                                                                 &lastWriteTime))
  339.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, destNamePtr);
  340.             if (!CloseHandle (destFileHandle))
  341.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, destFileHandle);
  342.          }
  343.  
  344.         if ((fileAttributes & (LOCK_MASK + RESOURCE_MASK)) != 0) {
  345.             destFileAttributes = GetFileAttributes (destNamePtr);
  346.             if (destFileAttributes == -1)
  347.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, destNamePtr);
  348.             if ((fileAttributes & RESOURCE_MASK) != 0) {
  349.                 sourceFileAttributes = GetFileAttributes (sourceNamePtr);
  350.                 if (sourceFileAttributes != -1)
  351.                     destFileAttributes =
  352.                         (destFileAttributes & ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) |
  353.                         (sourceFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM));
  354.             }
  355.             if ((fileAttributes & LOCK_MASK) != 0)
  356.                 destFileAttributes |= FILE_ATTRIBUTE_READONLY;
  357.             if (!SetFileAttributes (destNamePtr, destFileAttributes))
  358.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, destNamePtr);
  359.         }
  360.     }
  361. }
  362. #endif
  363.  
  364. #ifdef DF_MACHINE_NEXT
  365. void
  366. CopyFileAttributes (const char *sourceNamePtr, const char *destNamePtr, const int fileAttributes)
  367. {
  368.     struct stat         destStatBuffer;
  369.     int                         ioResult;
  370.     int                         sourceIOResult;
  371.   struct timeval    timeValue[2];
  372.     struct stat         sourceStatBuffer;
  373.  
  374.     if (fileAttributes != 0) {
  375.         sourceIOResult = 0;
  376.         if ((fileAttributes & (LOCK_MASK + RESOURCE_MASK)) != 0) {
  377.             ioResult = stat(destNamePtr, &destStatBuffer);
  378.             if (ioResult != 0)
  379.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, destNamePtr);
  380.         }
  381.  
  382.         if ((fileAttributes & (DATE_MASK + RESOURCE_MASK)) != 0)
  383.             sourceIOResult = stat(sourceNamePtr, &sourceStatBuffer);
  384.  
  385.         if (sourceIOResult == 0 && (fileAttributes & RESOURCE_MASK) != 0)
  386.             destStatBuffer.st_mode = (destStatBuffer.st_mode & ~0111) + (sourceStatBuffer.st_mode & 0111);
  387.  
  388.         if ((fileAttributes & LOCK_MASK) != 0)
  389.             destStatBuffer.st_mode &= ~0222;
  390.  
  391.         if (sourceIOResult == 0 && (fileAttributes & DATE_MASK) != 0) {
  392.             timeValue[0].tv_sec = sourceStatBuffer.st_atime;
  393.             timeValue[0].tv_usec = 0;
  394.             timeValue[1].tv_sec = sourceStatBuffer.st_mtime;
  395.             timeValue[1].tv_usec = 0;
  396.           ioResult = utimes (destNamePtr, timeValue);
  397.             if (ioResult != 0)
  398.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, destNamePtr);
  399.         }
  400.  
  401.         if ((fileAttributes & (LOCK_MASK + RESOURCE_MASK)) != 0) {
  402.             ioResult = chmod (destNamePtr, destStatBuffer.st_mode);
  403.             if (ioResult != 0)
  404.                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, destNamePtr);
  405.         }
  406.     }
  407. }
  408. #endif
  409.  
  410. FILE *
  411. CreateTypedFile (const char *pathCharPtr, int binary)
  412.  /*
  413.     * It is well known that getting and setting the file type after creating the file
  414.     * is non-atomic and suffers from potential race conditions.  There is no
  415.     * alternative because we are required to return a FILE *.
  416.     */
  417. {
  418.     char                         *fileModeCharPtr;
  419.     FILE                         *filePtr;
  420.     #ifdef DF_MACHINE_MACINTOSH
  421.         HParamBlockRec    hParamBlock;
  422.         int                         ioResult;
  423.         Str255                    pathNamePString;
  424.         int                            pathLength;
  425.     #endif
  426.  
  427.     #ifdef DF_MACHINE_MACINTOSH
  428.         SpinCursor (1);
  429.         bzero (&hParamBlock, sizeof (HParamBlockRec));
  430.         pathLength = strlen (pathCharPtr);
  431.         if (pathLength > FILENAME_MAX)
  432.             return (BAD_FILE_TYPE);
  433.         pathNamePString [0] = (unsigned char) pathLength;
  434.         memcpy (pathNamePString + 1, pathCharPtr, pathLength);
  435.         hParamBlock.fileParam.ioNamePtr = pathNamePString;
  436.     #endif
  437.     fileModeCharPtr = "w";
  438.     if (binary)
  439.         fileModeCharPtr = "wb";
  440.     filePtr = fopen (pathCharPtr, fileModeCharPtr);
  441.     if (filePtr == NULL)
  442.         ErrorWithStringArgument (CANT_CREATE_FILE, pathCharPtr);
  443.     #ifdef DF_MACHINE_MACINTOSH
  444.         ioResult = PBHGetFInfoSync (&hParamBlock);
  445.         if (ioResult != noErr)
  446.             ErrorWithStringArgument (UNEXPECTED_IO_ERROR, pathCharPtr);
  447.         hParamBlock.fileParam.ioVRefNum = 0;
  448.         hParamBlock.fileParam.ioDirID = 0;
  449.         if (binary) {
  450.             hParamBlock.fileParam.ioFlFndrInfo.fdCreator = (OSType) '\?\?\?\?';
  451.             hParamBlock.fileParam.ioFlFndrInfo.fdType = (OSType) '\?\?\?\?';
  452.         } else /* ASCII */
  453.             hParamBlock.fileParam.ioFlFndrInfo.fdCreator = (OSType) 'MPS ';
  454.         ioResult = PBHSetFInfoSync (&hParamBlock);
  455.         if (ioResult != noErr)
  456.             ErrorWithStringArgument (UNEXPECTED_IO_ERROR, pathCharPtr);
  457.     #endif
  458.     return (filePtr);
  459. }
  460.  
  461. /*
  462.  * A machine dependent routine that tests a path and returns what it can figure out
  463.  * about what type of file it is
  464.  */
  465.  
  466. #ifdef DF_MACHINE_MACINTOSH
  467.     int
  468.     FileType (const char *pathCharPtr)
  469.     {
  470.         OSErr                ioResult;
  471.         CInfoPBRec    paramBlk;
  472.         int                    pathLength;
  473.         Str255            pathNamePString;
  474.     
  475.         bzero (¶mBlk, sizeof (CInfoPBRec));
  476.         pathLength = strlen (pathCharPtr);
  477.         if (pathLength > FILENAME_MAX)
  478.             return (BAD_FILE_TYPE);
  479.         pathNamePString [0] = (unsigned char) pathLength;
  480.         memcpy (pathNamePString + 1, pathCharPtr, pathLength);
  481.         paramBlk.hFileInfo.ioNamePtr = pathNamePString;
  482.         ioResult = PBGetCatInfoSync (¶mBlk);
  483.         if (ioResult != noErr)
  484.             return (BAD_FILE_TYPE);
  485.  
  486.       if ((paramBlk.hFileInfo.ioFlAttrib & 0X10) != 0)
  487.             return (DIRECTORY_TYPE);
  488.         if (paramBlk.hFileInfo.ioFlFndrInfo.fdType == (OSType) 'TEXT')
  489.             return (TEXT_TYPE);
  490.         return (BINARY_TYPE);
  491.     }
  492. #endif
  493.  
  494. #ifndef DF_MACHINE_MACINTOSH
  495.     int
  496.     FileType (const char *pathCharPtr)
  497.     {
  498.         #ifdef DF_MACHINE_WINDOWS
  499.         {
  500.             DWORD                        fileAttributes;
  501.  
  502.             fileAttributes = GetFileAttributes (pathCharPtr);
  503.             if (fileAttributes == -1)
  504.                 return (BAD_FILE_TYPE);
  505.             if (fileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  506.                 return (DIRECTORY_TYPE);
  507.         }
  508.         #elif defined (DF_MACHINE_NEXT)
  509.         {
  510.             int                         ioResult;
  511.             struct stat         statBuffer;
  512.  
  513.             ioResult = stat(pathCharPtr, &statBuffer);
  514.             if (ioResult != 0)
  515.                 return (BAD_FILE_TYPE);
  516.             if ((statBuffer.st_mode & S_IFMT) == S_IFDIR)
  517.                 return (DIRECTORY_TYPE);
  518.         }
  519.         #endif
  520.         if (PathHasExtension (pathCharPtr, binaryExtensions))
  521.             return (BINARY_TYPE);
  522.         return (TEXT_TYPE);
  523.     }
  524. #endif
  525.  
  526. /* Split the file into lines, simultaneously computing the hash codes for each line. */
  527.  
  528. void
  529. find_and_hash_each_line (register struct file_data *filePtr)
  530. {
  531.     unsigned char                 c;
  532.     char                                 *endCharPtr;
  533.     unsigned                            h;
  534.     unsigned char              *ip;
  535.     unsigned char              *p;
  536.  
  537.     p = (unsigned char *) filePtr->buffer;
  538.  
  539.     if (filePtr->buffer != NULL) {
  540.         /* Attempt to get a good initial guess as to the number of lines. */
  541.         if (filePtr->linbuf == NULL) {
  542.             filePtr->linbufsize = filePtr->buffered_chars / 50 + 5;
  543.             filePtr->linbuf = (struct line_def *) xmalloc (filePtr->linbufsize * sizeof (struct line_def));
  544.         }
  545.  
  546.         filePtr->buffered_lines = 0;
  547.         endCharPtr = filePtr->buffer + filePtr->buffered_chars;
  548.  
  549.         while ((char *) p < endCharPtr)
  550.             {
  551.                 h = 0;
  552.                 ip = p;
  553.  
  554.                 if (filePtr->prefix_end <= (char *) p && filePtr->suffix_begin >= (char *) p)
  555.                     {
  556.                         /* Hash this line until we find a newline. */
  557.                         while ((c = *p) != '\n')
  558.                             {
  559.                                 h = HASH (h, c);
  560.                                 ++p;
  561.                             }
  562.                     }
  563.                 else
  564.                     /* This line is part of the matching prefix,
  565.                          so we don't need to hash it.  */
  566.                     while (*p != '\n')
  567.                         ++p;
  568.  
  569.                 /* Maybe increase the size of the line table. */
  570.                 if (filePtr->buffered_lines >= filePtr->linbufsize)
  571.                     {
  572.                         while (filePtr->buffered_lines >= filePtr->linbufsize)
  573.                             filePtr->linbufsize *= 2;
  574.                         filePtr->linbuf = (struct line_def *) xrealloc (filePtr->linbuf,
  575.                                                                                                                         filePtr->linbufsize * sizeof (struct line_def));
  576.                     }
  577.                 filePtr->linbuf[filePtr->buffered_lines].text = (char *) ip;
  578.                 filePtr->linbuf[filePtr->buffered_lines].length = p - ip + 1;
  579.                 filePtr->linbuf[filePtr->buffered_lines].hash = h;
  580.                 ++filePtr->buffered_lines;
  581.                 ++p;
  582.             }
  583.  
  584.         filePtr->linbufsize = filePtr->buffered_lines;
  585.         filePtr->linbuf = (struct line_def *) xrealloc (filePtr->linbuf,
  586.                                                                                                         filePtr->linbufsize * sizeof (struct line_def));
  587.  
  588. /*
  589.         The following if statement from the GNU sources doesn't work when merging files that
  590.         don't end in a return and no changes are detected.  I've modified the if as shown below
  591.         and I'm not 100% sure that this change won't introduce bugs, however it does pass my
  592.         test suite. -- DJA
  593.  
  594.         if (filePtr->missing_newline && filePtr->suffix_begin == endCharPtr)
  595. */
  596.  
  597.         if (filePtr->missing_newline &&  filePtr->buffer[filePtr->buffered_chars-1] == '\n')
  598.             --filePtr->linbuf[filePtr->buffered_lines - 1].length;
  599.     }
  600. }
  601.  
  602. /* Find the equiv class associated with all lines in filePtr.  */
  603.  
  604. void
  605. find_equiv_class (register struct file_data *filePtr)
  606. {
  607.     struct equivclass      *b;
  608.     int                                     bucket;
  609.     int                                     equivsValue;
  610.     unsigned int                     lineIndex;
  611.     struct equivclass      *p;
  612.  
  613.     if (filePtr->equivs == NULL)
  614.         filePtr->equivs = (int *) xmalloc (filePtr->buffered_lines * sizeof (int));
  615.  
  616.     for (lineIndex = 0; lineIndex < filePtr->buffered_lines; ++lineIndex)
  617.         {
  618.             p = NULL;
  619.             /* Equivalence class 0 is permanently allocated to lines that were
  620.                  not hashed because they were parts of identical prefixes or
  621.                  suffixes. */
  622.             if (lineIndex < filePtr->prefix_lines || filePtr->linbuf[lineIndex].text >= filePtr->suffix_begin)
  623.                 {
  624.                     equivsValue = 0;
  625.                     goto SetEquivsValue;
  626.                 }
  627.  
  628.             /* Check through the appropriate bucket to see if there isn't already
  629.                  an equivalence class for this line. */
  630.             bucket = filePtr->linbuf[lineIndex].hash % nbuckets;
  631.             b = buckets[bucket];
  632.             while (b)
  633.                 {
  634.                     if (b->line.hash == filePtr->linbuf[lineIndex].hash &&
  635.                             b->line.length == filePtr->linbuf[lineIndex].length &&
  636.                             !line_cmp (&b->line, &filePtr->linbuf[lineIndex]))
  637.                         {
  638.                             equivsValue = b - equivs;
  639.                             goto SetEquivsValue;
  640.                         }
  641.                     p = b, b = b->next;
  642.                 }
  643.  
  644.             /* Create a new equivalence class in this bucket. */
  645.  
  646.             p = &equivs[equivs_index++];
  647.             p->next = buckets[bucket];
  648.             buckets[bucket] = p;
  649.             p->line = filePtr->linbuf[lineIndex];
  650.             equivsValue = equivs_index - 1;
  651.  
  652. SetEquivsValue:
  653.             filePtr->equivs[lineIndex] = equivsValue;
  654.  
  655.         }
  656. }
  657.  
  658. /* Given a vector of two file_data objects, find the identical prefixes and suffixes of each object. */
  659.  
  660. static void
  661. find_identical_ends (register struct file_data *file0Ptr, register struct file_data *file1Ptr)
  662. {
  663.     char                                 *beg0;
  664.     char                                 *end0;
  665.     int                                     lines;
  666.     register char              *p0;
  667.     register char              *p1;
  668.  
  669.     if (file0Ptr->buffered_chars == 0 || file1Ptr->buffered_chars == 0)
  670.         {
  671.             file0Ptr->prefix_end = file0Ptr->buffer;
  672.             file1Ptr->prefix_end = file1Ptr->buffer;
  673.             file0Ptr->prefix_lines = file1Ptr->prefix_lines = 0;
  674.             file0Ptr->suffix_begin = file0Ptr->buffer + file0Ptr->buffered_chars;
  675.             file1Ptr->suffix_begin = file1Ptr->buffer + file1Ptr->buffered_chars;
  676.             file0Ptr->suffix_lines = file1Ptr->suffix_lines = 0;
  677.             return;
  678.         }
  679.  
  680.     /* Find identical prefix.  */
  681.  
  682.     p0 = file0Ptr->buffer;
  683.     p1 = file1Ptr->buffer;
  684.     lines = 0;
  685.  
  686.     /* Insert end "sentinels", in this case characters that are guaranteed
  687.          to make the equality test false, and thus terminate the loop.    */
  688.  
  689.     if (file0Ptr->buffered_chars < file1Ptr->buffered_chars)
  690.         p0[file0Ptr->buffered_chars] = (char) ~p1[file0Ptr->buffered_chars];
  691.     else
  692.         p1[file1Ptr->buffered_chars] = (char) ~p0[file1Ptr->buffered_chars];
  693.  
  694.     /* Loop until first mismatch, or to the sentinel characters.    */
  695.     while (1)
  696.         {
  697.             char c = *p0++;
  698.             if (c != *p1++)
  699.                 break;
  700.             if (c == '\n')
  701.                 ++lines;
  702.         }
  703.  
  704.     /* Don't count missing newline as part of prefix. */
  705.     if ( (file0Ptr->missing_newline && (unsigned int) (p0 - file0Ptr->buffer) > file0Ptr->buffered_chars) ||
  706.              (file1Ptr->missing_newline && (unsigned int) (p1 - file1Ptr->buffer) > file1Ptr->buffered_chars) )
  707.         --p0, --p1, --lines;
  708.  
  709.     /* If the sentinel was passed, and lengths are equal, the
  710.          files are identical. */
  711.  
  712.     if ((unsigned int) (p0 - file0Ptr->buffer) > file0Ptr->buffered_chars
  713.             && file0Ptr->buffered_chars == file1Ptr->buffered_chars)
  714.         {
  715.             file0Ptr->prefix_end = p0 - 1;
  716.             file1Ptr->prefix_end = p1 - 1;
  717.             file0Ptr->prefix_lines = file1Ptr->prefix_lines = lines;
  718.             file0Ptr->suffix_begin = file0Ptr->buffer;
  719.             file1Ptr->suffix_begin = file1Ptr->buffer;
  720.             file0Ptr->suffix_lines = file1Ptr->suffix_lines = lines;
  721.             return;
  722.         }
  723.  
  724.     /* Point at first nonmatching characters.  */
  725.     --p0, --p1;
  726.  
  727.     /* Skip back to last line-beginning in the prefix.    */
  728.     while (p0 != file0Ptr->buffer && p0[-1] != '\n')
  729.         --p0, --p1;
  730.  
  731.     /* Record the prefix.  */
  732.     file0Ptr->prefix_end = p0;
  733.     file1Ptr->prefix_end = p1;
  734.     file0Ptr->prefix_lines = file1Ptr->prefix_lines = lines;
  735.     
  736.     /* Find identical suffix.  */
  737.  
  738.     /* P0 and P1 point beyond the last chars not yet compared.    */
  739.     p0 = file0Ptr->buffer + file0Ptr->buffered_chars;
  740.     p1 = file1Ptr->buffer + file1Ptr->buffered_chars;
  741.     lines = 0;
  742.  
  743.     if (file0Ptr->missing_newline == file1Ptr->missing_newline)
  744.         {
  745.             end0 = p0;                                /* Addr of last char in file 0.  */
  746.  
  747.             /* Get value of P0 at which we should stop scanning backward:
  748.                  this is when either P0 or P1 points just past the last char
  749.                  of the identical prefix.  */
  750.             if (file0Ptr->buffered_chars < file1Ptr->buffered_chars)
  751.                 beg0 = file0Ptr->prefix_end;
  752.             else
  753.                 /* Figure out where P0 will be when P1 is at the end of the prefix.
  754.                      Thus we only need to test P0.    */
  755.                 beg0 = (file0Ptr->prefix_end + file0Ptr->buffered_chars - file1Ptr->buffered_chars);
  756.  
  757.             /* Scan back until chars don't match or we reach that point.  */
  758.             while (p0 != beg0)
  759.                 {
  760.                     char c = *--p0;
  761.                     if (c != *--p1)
  762.                         {
  763.                             /* Point at the first char of the matching suffix.    */
  764.                             ++p0, ++p1;
  765.                             break;
  766.                         }
  767.                     if (c == '\n')
  768.                         ++lines;
  769.                 }
  770.  
  771.             /* Are we at a line-beginning in both files?    */
  772.             if (p0 != end0
  773.                     && !((p0 == file0Ptr->buffer || p0[-1] == '\n')
  774.                              &&
  775.                              (p1 == file1Ptr->buffer || p1[-1] == '\n')))
  776.                 {
  777.                     /* No.    We counted one line too many.  */
  778.                     --lines;
  779.                     /* Advance to next place that is a line-beginning in both files.    */
  780.                     do
  781.                         {
  782.                             ++p0, ++p1;
  783.                         }
  784.                     while (p0 != end0 && p0[-1] != '\n');
  785.                 }
  786.         }
  787.  
  788.     /* Record the suffix.  */
  789.     file0Ptr->suffix_begin = p0;
  790.     file1Ptr->suffix_begin = p1;
  791.     file0Ptr->suffix_lines = file1Ptr->suffix_lines = lines;
  792. }
  793.  
  794. IgnoreFile (const char *nameSuffixPtr)
  795. {
  796.         return (*nameSuffixPtr == '.' || PathHasExtension (nameSuffixPtr, ignoreExtensions));
  797. }
  798.  
  799. /*
  800.  * A machine dependent routine that creates a directory.    The result returned is zero if
  801.  * it succeeds otherwise non-zero.
  802.  */
  803.  
  804. #ifdef DF_MACHINE_MACINTOSH
  805. int
  806. MakeDirectory (const char *pathCharPtr)
  807. {
  808.     OSErr                        ioResult;
  809.     HParamBlockRec    paramBlk;
  810.     int                            pathLength;
  811.     Str255                    pathNamePString;
  812.  
  813.     bzero (¶mBlk, sizeof (HParamBlockRec));
  814.     pathLength = strlen (pathCharPtr);
  815.     if (pathLength > 255)
  816.         return (BAD_FILE_TYPE);
  817.     pathNamePString [0] = (unsigned char) pathLength;
  818.     memcpy (pathNamePString + 1, pathCharPtr, pathLength);
  819.  
  820.     paramBlk.ioParam.ioNamePtr = pathNamePString;
  821.     ioResult = PBDirCreateSync (¶mBlk);
  822.     if (ioResult != noErr)
  823.         return (-1);
  824.     else
  825.         return (0);
  826. }
  827. #endif
  828.  
  829. #ifdef DF_MACHINE_WINDOWS
  830. int
  831. MakeDirectory (const char *pathCharPtr)
  832. {
  833.     if (!CreateDirectory (pathCharPtr, (LPSECURITY_ATTRIBUTES) NULL))
  834.         return (-1);
  835.     return (0);
  836. }
  837. #endif
  838.  
  839. #ifdef DF_MACHINE_NEXT
  840. int
  841. MakeDirectory (const char *pathCharPtr)
  842. {
  843.     return (mkdir(pathCharPtr, 0777));
  844. }
  845. #endif
  846.  
  847. int
  848. PathHasExtension (const char *pathCharPtr, const char **pathExtensions)
  849. {
  850.     int                                        index;
  851.     char                                    lowerCaseExtension [MAX_EXTENSION_LENGTH + 1];
  852.     register char                 *lowerCaseExtensionPtr;
  853.     register const char    **pathExtensionsPtr;
  854.     register char                 *testExtensionPtr;
  855.     char                                    theChar;
  856.  
  857.     testExtensionPtr = strrchr (pathCharPtr, '.');
  858.     if (testExtensionPtr == NULL)
  859.         return (FALSE);
  860.     testExtensionPtr ++;
  861.  
  862.     index = 0;
  863.     lowerCaseExtensionPtr = lowerCaseExtension;
  864.     do {
  865.         if (index > MAX_EXTENSION_LENGTH)
  866.             return (FALSE); /* Extension too long */
  867.         theChar = tolower (*testExtensionPtr++);
  868.         *lowerCaseExtensionPtr++ = theChar;
  869.         index++;
  870.     } while (theChar !=    '\0');
  871.     pathExtensionsPtr = pathExtensions;
  872.     while (**pathExtensionsPtr) {
  873.         if (strcmp (*pathExtensionsPtr, lowerCaseExtension) == 0)
  874.             return (TRUE);
  875.         pathExtensionsPtr++;        
  876.     }                
  877.     return (FALSE);
  878. }
  879.  
  880. /*
  881.  * A machine dependent routine that reads a list of filenames from a directory.  A pointer
  882.  * to a contiguous list of null terminated strings is returned.  The list of strings is
  883.  * terminated with "".    The caller is responsible for freeing the pointer that is returned.
  884.  * Returns NULL if an error occurs.
  885.  */
  886.  
  887. #ifdef DF_MACHINE_MACINTOSH
  888. fileNameListPType
  889. ReadDirectory (const char *pathCharPtr)
  890. {
  891.     long                            ioDirID;
  892.     OSErr                            ioResult;
  893.     short                            ioVRefNum;
  894.     int                                nameLength;
  895.     fileNameListPType nameListPtr;
  896.     int                             nameListLength;
  897.     int                             nameListTotalSpace;
  898.     CInfoPBRec                paramBlk;
  899.     int                                pathLength;
  900.     Str255                        pathNamePString;
  901.  
  902.     bzero (¶mBlk, sizeof (CInfoPBRec));
  903.     pathLength = strlen (pathCharPtr);
  904.     if (pathLength > 255)
  905.         return (NULL);
  906.     pathNamePString [0] = (unsigned char) pathLength;
  907.     memcpy (pathNamePString + 1, pathCharPtr, pathLength);
  908.     paramBlk.hFileInfo.ioNamePtr = pathNamePString;
  909.     ioResult = PBGetCatInfoSync (¶mBlk);
  910.     if (ioResult != noErr || (paramBlk.hFileInfo.ioFlAttrib & 0X10) == 0)
  911.         return (NULL);
  912.  
  913. /*
  914.  * Set nameListLength to the size of a name list with no elements in it.  Since ANSI
  915.  * C requires us to put one item in the array we use offsetof to get the size.
  916.  */
  917.     nameListLength = offsetof (fileNameListType, names);
  918.     nameListTotalSpace = 4096;
  919.     nameListPtr = xmalloc (nameListTotalSpace);
  920.     nameListPtr->numberOfItems = 0;
  921.     ioVRefNum = paramBlk.hFileInfo.ioVRefNum;
  922.     ioDirID = paramBlk.hFileInfo.ioDirID;
  923.     do {
  924.         paramBlk.hFileInfo.ioFDirIndex ++;
  925.         paramBlk.hFileInfo.ioVRefNum = ioVRefNum;
  926.         paramBlk.hFileInfo.ioDirID = ioDirID;
  927.         ioResult = PBGetCatInfoSync (¶mBlk);
  928.         if (ioResult == fnfErr)
  929.             break;
  930.         if (ioResult != noErr)
  931.             Error (UNEXPECTED_IO_ERROR);
  932.         nameLength = pathNamePString[0];
  933.         if (nameLength != 0) {
  934.      /*
  935.         * We add 2 to the length: one for null termination of this file name and one for
  936.         * possible end of list null termination, which avoids a rare realloc.
  937.         */
  938.             if (nameListLength + nameLength + 2 > nameListTotalSpace) {
  939.                 nameListTotalSpace += nameListLength + nameLength + 2048;
  940.                 nameListPtr = xrealloc (nameListPtr, nameListTotalSpace);
  941.             }
  942.             memcpy ((char *) nameListPtr + nameListLength,
  943.                             (char *) pathNamePString + 1,
  944.                             nameLength);
  945.             nameListLength += nameLength;
  946.             *((char *) nameListPtr + nameListLength) = '\0';
  947.             nameListLength++;
  948.             nameListPtr->numberOfItems++;
  949.         }
  950.     } while (TRUE);
  951.     *((char *) nameListPtr + nameListLength) = '\0';
  952.     nameListLength++;
  953.  
  954.     nameListPtr = xrealloc (nameListPtr, nameListLength);
  955.  
  956.     return (nameListPtr);
  957. }
  958. #endif
  959.  
  960. #ifdef DF_MACHINE_WINDOWS
  961. fileNameListPType
  962. ReadDirectory (const char *pathCharPtr)
  963. {
  964.     char                            fileSearchPath [FILENAME_MAX];
  965.     WIN32_FIND_DATA        findData;
  966.     int                                nameLength;
  967.     int                             nameListLength;
  968.     fileNameListPType nameListPtr;
  969.     int                             nameListTotalSpace;
  970.     HANDLE                        searchHandle;
  971.  
  972.     if (strlen (pathCharPtr) >= FILENAME_MAX - 2) {
  973.         fprintf (stderr, "%s\n", pathCharPtr);
  974.         Error (PATH_TOO_LONG);
  975.     }
  976.     strcpy (fileSearchPath, pathCharPtr);
  977.     strcat (fileSearchPath, "\\*");    
  978.     searchHandle = FindFirstFile (fileSearchPath, &findData);
  979.     if (searchHandle == INVALID_HANDLE_VALUE)
  980.         return (NULL);
  981.     /*
  982.      * Read past the first two directories: "." and ".."
  983.      */
  984.     if (!FindNextFile (searchHandle, &findData))
  985.         ErrorWithStringArgument (UNEXPECTED_IO_ERROR, pathCharPtr);
  986.     /*
  987.      * Set nameListLength to the size of a name list with no elements in it.  Since ANSI
  988.      * C requires us to put one item in the array we use offsetof to get the size.
  989.      */
  990.     nameListLength = offsetof (fileNameListType, names);
  991.     nameListTotalSpace = 4096;
  992.     nameListPtr = xmalloc (nameListTotalSpace);
  993.     nameListPtr->numberOfItems = 0;
  994.     while (FindNextFile (searchHandle, &findData)) {
  995.         if ((findData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) == 0) {
  996.             nameLength = strlen (findData.cFileName);
  997.          /*
  998.             * We add 2 to the length: one for null termination of this file name and one for
  999.             * possible end of list null termination, which avoids a rare realloc.
  1000.             */
  1001.             if (nameListLength + nameLength + 2 > nameListTotalSpace) {
  1002.                 nameListTotalSpace += nameListLength + nameLength + 2048;
  1003.                 nameListPtr = xrealloc (nameListPtr, nameListTotalSpace);
  1004.             }
  1005.             memcpy ((char *) nameListPtr + nameListLength, findData.cFileName, nameLength);
  1006.             nameListLength += nameLength;
  1007.             *((char *) nameListPtr + nameListLength) = '\0';
  1008.             nameListLength++;
  1009.             nameListPtr->numberOfItems++;
  1010.         }
  1011.     }
  1012.     *((char *) nameListPtr + nameListLength) = '\0';
  1013.     nameListLength++;
  1014.  
  1015.     nameListPtr = xrealloc (nameListPtr, nameListLength);
  1016.     if (!FindClose (searchHandle))
  1017.         ErrorWithStringArgument (UNEXPECTED_IO_ERROR, pathCharPtr);
  1018.  
  1019.     return (nameListPtr);
  1020. }
  1021. #endif
  1022.  
  1023. #ifdef DF_MACHINE_NEXT
  1024. fileNameListPType
  1025. ReadDirectory (const char *pathCharPtr)
  1026. {
  1027.     DIR                                      *directoryPtr;
  1028.     register struct direct *entryPtr;
  1029.     fileNameListPType                nameListPtr;
  1030.     int                                         nameListLength;
  1031.     int                                         nameListTotalSpace;
  1032.  
  1033.     directoryPtr = opendir(pathCharPtr);
  1034.     if (directoryPtr == NULL)
  1035.         return (NULL);
  1036. /*
  1037.  * Read past the first two directories: "." and ".."
  1038.  */
  1039.     entryPtr = readdir (directoryPtr);
  1040.     entryPtr = readdir (directoryPtr);
  1041.  
  1042. /*
  1043.  * Set nameListLength to the size of a name list with no elements in it.  Since ANSI
  1044.  * C requires us to put one item in the array we use offsetof to get the size.
  1045.  */
  1046.     nameListLength = offsetof (fileNameListType, names);
  1047.     nameListTotalSpace = 4096;
  1048.     nameListPtr = xmalloc (nameListTotalSpace);
  1049.     nameListPtr->numberOfItems = 0;
  1050.     do {
  1051.         entryPtr = readdir (directoryPtr);
  1052.         if (entryPtr == NULL)
  1053.             break;
  1054.         if (entryPtr->d_namlen != 0) {
  1055.      /*
  1056.         * We add 2 to the length: one for null termination of this file name and one for
  1057.         * possible end of list null termination, which avoids a rare realloc.
  1058.         */
  1059.             if (nameListLength + entryPtr->d_namlen + 2 > nameListTotalSpace) {
  1060.                 nameListTotalSpace += nameListLength + entryPtr->d_namlen + 2048;
  1061.                 nameListPtr = xrealloc (nameListPtr, nameListTotalSpace);
  1062.             }
  1063.             memcpy ((char *) nameListPtr + nameListLength, entryPtr->d_name, entryPtr->d_namlen);
  1064.             nameListLength += entryPtr->d_namlen;
  1065.             *((char *) nameListPtr + nameListLength) = '\0';
  1066.             nameListLength++;
  1067.             nameListPtr->numberOfItems++;
  1068.         }
  1069.     } while (TRUE);
  1070.     *((char *) nameListPtr + nameListLength) = '\0';
  1071.     nameListLength++;
  1072.  
  1073.     nameListPtr = xrealloc (nameListPtr, nameListLength);
  1074.     closedir (directoryPtr);
  1075.  
  1076.     return (nameListPtr);
  1077. }
  1078. #endif
  1079.  
  1080. /* Given a vector of two file_data objects, read the file associated
  1081.      with each one, and build the table of equivalence classes.  */
  1082.  
  1083. void
  1084. read_files (register struct file_data *file0Ptr, register struct file_data *file1Ptr)
  1085. {
  1086.     int equivs_alloc;                         /* Size allocated to the array `equivs'. */
  1087.     int primes_index;                         /* Index of current nbuckets in primes. */
  1088.  
  1089.     slurp (file0Ptr);
  1090.     slurp (file1Ptr);
  1091.  
  1092.     find_identical_ends (file0Ptr, file1Ptr);
  1093.  
  1094.     find_and_hash_each_line (file0Ptr);
  1095.     find_and_hash_each_line (file1Ptr);
  1096.  
  1097.     if (file0Ptr->buffer != NULL && file1Ptr->buffer != NULL) {
  1098.         /* This is guaranteed to be enough space.  */
  1099.         equivs_alloc = file0Ptr->buffered_lines + file1Ptr->buffered_lines + 1;
  1100.         equivs = (struct equivclass *) xmalloc (equivs_alloc * sizeof (struct equivclass));
  1101.         /* Equivalence class 0 is permanently safe for lines that were not
  1102.              hashed.    Real equivalence classes start at 1. */
  1103.         equivs_index = 1;
  1104.     
  1105.         primes_index = 0;
  1106.         while (primes[primes_index] < equivs_alloc / 3)
  1107.             primes_index++;
  1108.  
  1109.         buckets = (struct equivclass **) xmalloc (primes[primes_index] * sizeof (struct equivclass *));
  1110.         bzero (buckets, primes[primes_index] * sizeof (struct equivclass *));
  1111.         nbuckets = primes[primes_index];
  1112.  
  1113.         find_equiv_class (file0Ptr);
  1114.         find_equiv_class (file1Ptr);
  1115.  
  1116.         file0Ptr->equiv_max = file1Ptr->equiv_max = equivs_index;
  1117.  
  1118.         free (equivs);
  1119.         free (buckets);
  1120.     }
  1121. }
  1122.  
  1123. /* Slurp the file completely into memory. */
  1124.  
  1125. void
  1126. slurp (struct file_data *filePtr)
  1127. {
  1128.     int     ioResult;
  1129.  
  1130.     #ifdef DF_MACHINE_MACINTOSH
  1131.         SpinCursor (1);
  1132.     #endif
  1133.     
  1134.     if (filePtr->desc == NULL /* file doesn't exist */) {
  1135.         filePtr->buffered_chars = 0;
  1136.         if (filePtr->buffer != NULL)
  1137.             free (filePtr->buffer);
  1138.         filePtr->buffer = NULL;
  1139.     } else { /* file exists */
  1140.         ioResult = fseek (filePtr->desc, 0, SEEK_END);
  1141.         if (ioResult != 0)
  1142.             ErrorWithStringArgument (UNEXPECTED_IO_ERROR, filePtr->namePtr);
  1143.  
  1144.         filePtr->bufsize = (int) ftell (filePtr->desc);
  1145.         if (filePtr->bufsize == -1)
  1146.             ErrorWithStringArgument (UNEXPECTED_IO_ERROR, filePtr->namePtr);
  1147.  
  1148.         ioResult = fseek (filePtr->desc, 0, SEEK_SET);
  1149.         if (ioResult != 0)
  1150.             ErrorWithStringArgument (UNEXPECTED_IO_ERROR, filePtr->namePtr);
  1151.  
  1152.      /* Leave room in the buffer for 2 extra chars beyond those
  1153.              that filePtr->bufsize describes:
  1154.              one for a newline (in case the text does not end with one)
  1155.              and one for a sentinel in find_identical_ends.  */
  1156.         if (filePtr->buffer == NULL)
  1157.             filePtr->buffer = (char *) xmalloc (filePtr->bufsize + 2);
  1158.         else
  1159.             filePtr->buffer = (char *) xrealloc (filePtr->buffer, filePtr->bufsize + 2);
  1160.         /*
  1161.          * We reset bufsize to the actual number of characters in the buffer rather
  1162.          * than the size malloced.  They may differ because we read text files.  For
  1163.          * example, text files can store end of lines as more than one character.
  1164.          */
  1165.         filePtr->buffered_chars = fread (filePtr->buffer, 1, filePtr->bufsize, filePtr->desc);
  1166.         if (ferror (filePtr->desc) != 0)
  1167.             ErrorWithStringArgument (UNEXPECTED_IO_ERROR, filePtr->namePtr);
  1168.         if (filePtr->buffered_chars != filePtr->bufsize) {
  1169.             filePtr->bufsize = filePtr->buffered_chars;
  1170.             filePtr->buffer = (char *) xrealloc (filePtr->buffer, filePtr->bufsize + 2);
  1171.         }
  1172.  
  1173.         /* Make sure text ends in a newline,
  1174.              but remember that we had to add one.  */
  1175.         if (filePtr->buffered_chars > 0 && filePtr->buffer[filePtr->buffered_chars - 1] != '\n') {
  1176.             filePtr->missing_newline = TRUE;
  1177.             filePtr->buffer[filePtr->buffered_chars++] = '\n';
  1178.         }
  1179.  
  1180.         /* Don't use uninitialized storage. */
  1181.         filePtr->buffer[filePtr->buffered_chars] = '\0';
  1182.     }
  1183. }
  1184.  
  1185.